﻿using Nx.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Nx.Clients.Http
{
    public abstract class WebApiClient : IWebApiClient
    {
        private const string JsonMediaType = "application/json";
        protected readonly ILogger _Logger;
        protected readonly ISerializer _Serializer;
        protected readonly string ServiceUrl;
        protected HttpClient _Client = new HttpClient();

        private bool _IsBusy;
        public bool IsBusy
        {
            get { return _IsBusy; }
            set
            {
                _IsBusy = value;
            }
        }

        public async Task<T> GetAsync<T>()
        {
            var result = await GetAsync<T>(string.Empty);
            return result;
        }

        public async Task<T> GetAsync<T>(string queryString)
        {
            T result = default(T);

            string jsonString = await GetStringAsync(queryString);

            if (jsonString != null)
            {
                result = _Serializer.Deserialize<T>(jsonString);
                _Logger.Debug("Operation returned an entity : {0}", result);
            }

            return result;
        }

        public async Task<IEnumerable<T>> GetCollectionAsync<T>()
        {
            var result = await GetCollectionAsync<T>(string.Empty);
            return result;
        }

        public async Task<IEnumerable<T>> GetCollectionAsync<T>(string queryString)
        {
            IEnumerable<T> result = Enumerable.Empty<T>();

            string @string = await GetStringAsync(queryString);

            if (@string != null)
            {
                result = _Serializer.DeserializeCollection<T>(@string);
                _Logger.Debug("Operation returned {0} entities", result.Count());
            }

            return result;
        }

        protected async Task<string> GetStringAsync(string queryString)
        {
            string result = null;
            IsBusy = true;

            string query = string.IsNullOrEmpty(queryString) ? ServiceUrl : string.Format(CultureInfo.InvariantCulture, "{0}{1}", ServiceUrl, queryString);
            _Logger.Debug("Executing GET {0}", query);

            try
            {
                HttpResponseMessage response = await _Client.GetAsync(query);

                if (IsOperationSuccessful(response))
                {
                    result = await response.Content.ReadAsStringAsync();
                }
            }
            catch (Exception ex)
            {
                _Logger.Error("Error while client was performing a GET operation : {0}", ex.Message);
                result = null;
            }
            finally
            {
                IsBusy = false;
            }

            return result;
        }

        public async Task<bool> TryPostAsync<T>(T dto)
        {
            bool result = true;

            var response = await PostJsonStringAsync(dto);
            if (!IsOperationSuccessful(response))
            {
                result = false;
            }

            return result;
        }

        public async Task<T> PostAsync<T>(T dto) where T : class
        {
            var response = await PostJsonStringAsync(dto);

            if (IsOperationSuccessful(response))
            {
                return _Serializer.Deserialize<T>(await response.Content.ReadAsStringAsync());
            }

            return null;
        }

        protected async Task<HttpResponseMessage> PostJsonStringAsync<T>(T dto)
        {
            IsBusy = true;

            try
            {
                string jsonString = _Serializer.Serialize(dto);
                var response = await ExecutePostAsync(jsonString);

                return response;
            }
            catch (Exception ex)
            {
                _Logger.Error("Error while client was performing a POST operation : {0}", ex.Message);
            }
            finally
            {
                IsBusy = false;
            }

            return null;
        }

        private bool IsOperationSuccessful(HttpResponseMessage message)
        {
            if (!message.IsSuccessStatusCode)
            {
                _Logger.Error("Operation failed : {0}", message.StatusCode);
            }
            else
            {
                _Logger.Debug("Operation successful");
            }

            return message.IsSuccessStatusCode;
        }

        private async Task<HttpResponseMessage> ExecuteGetAsync(string queryString)
        {
            _Logger.Debug("Executing GET {0}");
            return await _Client.GetAsync(queryString);
        }

        private async Task<HttpResponseMessage> ExecutePostAsync(string body)
        {
            _Logger.Debug("Executing POST {0}", ServiceUrl);
            return await _Client.PostAsync(ServiceUrl, new StringContent(body, Encoding.UTF8, JsonMediaType));
        }

        public async Task<bool> Delete(string id)
        {
            bool result = true;
            _Logger.Debug("Executing DELETE {0}/{1}", ServiceUrl, id);
            var response = await _Client.DeleteAsync(string.Format("{0}/{1}", ServiceUrl, id));
            if (!IsOperationSuccessful(response))
            {
                result = false;
            }

            return result;
        }

        public async Task<bool> Update<T>(T dto)
            where T : class
        {
            bool result = true;
            string jsonString = _Serializer.Serialize(dto);
            _Logger.Debug("Executing UPDATE {0}/{1}");

            var response = await _Client.PutAsync(ServiceUrl, new StringContent(jsonString, Encoding.UTF8, JsonMediaType));
            if (!IsOperationSuccessful(response))
            {
                result = false;
            }

            return result;
        }

        public WebApiClient(ILogFactory logFactory, ISerializer serializer, string serviceUrl)
        {
            _Logger = logFactory.CreateLogger("WebApiClientLogger");
            _Serializer = serializer;
            ServiceUrl = serviceUrl;
        }

        ~WebApiClient()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_Client != null)
                {
                    _Client.CancelPendingRequests();
                    _Client.Dispose();
                }

                _Logger.Dispose();
                _Serializer.Dispose();
            }
        }
    }
}
